package drds.server.memory.unsafe.ringbuffer.common.sequence;

import drds.server.memory.unsafe.Platform;

/**
 * @author lmax.Disruptor
 * @version 3.3.5
 * @date 2016/7/23
 */
class LhsPadding {
	protected long p1, p2, p3, p4, p5, p6, p7;
}

class Value extends LhsPadding {
	protected volatile long value;
}

class RhsPadding extends Value {
	protected long p9, p10, p11, p12, p13, p14, p15;
}

/**
 * <p>
 * Concurrent sequence class used for tracking the progress of the ring buffer
 * and event processors. Support a number of concurrent operations including CAS
 * and order writes.
 * 
 * <p>
 * Also attempts to be more efficient with regards to false sharing by adding
 * padding around the volatile field.
 */
public class Sequence extends RhsPadding {
	public static final long INITIAL_VALUE = -1L;
	private static final long VALUE_OFFSET;

	static {
		try {
			VALUE_OFFSET = Platform.objectFieldOffset(Value.class.getDeclaredField("value"));
		} catch (final Exception e) {
			throw new RuntimeException(e);
		}
	}

	/**
	 * Create a sequence initialised to -1.
	 */
	public Sequence() {
		this(INITIAL_VALUE);
	}

	/**
	 * Create a sequence with a specified initial value.
	 * 
	 * @param initialValue
	 *            The initial value for this sequence.
	 */
	public Sequence(final long initialValue) {
		Platform.putOrderedLong(this, VALUE_OFFSET, initialValue);
	}

	/**
	 * Perform a volatile read of this sequence's value.
	 * 
	 * @return The current value of the sequence.
	 */
	public long get() {
		return value;
	}

	/**
	 * Perform an ordered write of this sequence. The intent is a Store/Store
	 * barrier between this write and any previous store.
	 * 
	 * @param value
	 *            The new value for the sequence.
	 */
	public void set(final long value) {
		Platform.putOrderedLong(this, VALUE_OFFSET, value);
	}

	/**
	 * Performs a volatile write of this sequence. The intent is a Store/Store
	 * barrier between this write and any previous write and a Store/Load
	 * barrier between this write and any subsequent volatile read.
	 * 
	 * @param value
	 *            The new value for the sequence.
	 */
	public void setVolatile(final long value) {
		Platform.putLongVolatile(this, VALUE_OFFSET, value);
	}

	/**
	 * Perform a compare and set operation on the sequence.
	 * 
	 * @param expectedValue
	 *            The expected current value.
	 * @param newValue
	 *            The value to update to.
	 * @return true if the operation succeeds, false otherwise.
	 */
	public boolean compareAndSet(final long expectedValue, final long newValue) {
		return Platform.compareAndSwapLong(this, VALUE_OFFSET, expectedValue, newValue);
	}

	/**
	 * Atomically increment the sequence by one.
	 * 
	 * @return The value after the increment
	 */
	public long incrementAndGet() {
		return addAndGet(1L);
	}

	/**
	 * Atomically add the supplied value.
	 * 
	 * @param increment
	 *            The value to add to the sequence.
	 * @return The value after the increment.
	 */
	public long addAndGet(final long increment) {
		long currentValue;
		long newValue;

		do {
			currentValue = get();
			newValue = currentValue + increment;
		} while (!compareAndSet(currentValue, newValue));

		return newValue;
	}

	@Override
	public String toString() {
		return Long.toString(get());
	}
}
